#import "ValidatingDelegate.h"
#import "KeyValueValidation.h"

@implementation ValidatingDelegate

- (void)setValidatesImmediately:(BOOL)yn
{
    validatesImmediately = yn;
}

- (BOOL)validatesImmediately
{
    return validatesImmediately;
}

- (void)reportValidationError:(NSString *)message forKey:(NSString *)key ofObject:object inController:(EOController *)controller
{
    NSString *title;
    title = [NSString stringWithFormat:@"Invalid input for: %@", key];

    // Let's make sure that the object with the error is selected
    [controller setSelectionIndexes:[NSArray arrayWithObject:
        [NSNumber numberWithInt:[[controller allObjects] indexOfObjectIdenticalTo:object]]]];

    NXRunAlertPanel([title cString], [message cString], "OK", NULL, NULL);

    // we could tell the controller to select the invalid object and
    // run through the associations and restore focus to the one
    // matching the key
}

- (void)reportValidationErrors:(NSDictionary *)errors forObject:object inController:(EOController *)controller
{
    // pick one of the errors and display it
    NSEnumerator *errorEnum = [errors keyEnumerator];
    NSString *badValueKey, *errorMessage;
    badValueKey = [errorEnum nextObject];
    errorMessage = [errors objectForKey:badValueKey];

    [self reportValidationError:errorMessage forKey:badValueKey ofObject:object
        inController:controller];
}

- (NSDictionary *)controller:(EOController *)controller willSaveEdits: (NSDictionary *)edits toObject:object;
{
    if (validatesImmediately) {
        NSMutableDictionary *converted = [NSMutableDictionary dictionaryWithCapacity:[edits count]];
        NSEnumerator *editEnum = [edits keyEnumerator];
        NSString *key;
        NSDictionary *errors;

        while (key = [editEnum nextObject]) {
            // construct a selector and see if the have a validation method
            id value = [[controller dataSource] coerceValue:[edits objectForKey:key] forKey:key];
            if (!value) {
                [self reportValidationError:@"Value entered is not of correct type"
                    forKey:key ofObject:object inController:controller];
                return nil;
            }

            [converted setObject:value forKey:key];
        }

        errors = [object validateValuesInDictionary:converted];
        if([errors count]) {
            [self reportValidationErrors:errors forObject:object inController:controller];
            // [controller discardEdits];  // throw away the bad values
            return nil;
        }
        return converted;
    }
    return edits;
}


- (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
    willInsertObject:object
    inDataSource:dataSource
{
    if ([object respondsToSelector:@selector(validForInsertInDataSource:)]) {
        NSDictionary *errors = [object validForInsertInDataSource:dataSource];
        if (errors) {
            [self reportValidationErrors:errors forObject:object inController:controller];
            return EORollbackDataSourceOperation;
        }
    }
    return EOPerformDataSourceOperation;
}

- (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
    willUpdateObject:object
    inDataSource:dataSource
{
    if ([object respondsToSelector:@selector(validForUpdateInDataSource:)]) {
        NSDictionary *errors = [object validForUpdateInDataSource:dataSource];
        if (errors) {
            [self reportValidationErrors:errors forObject:object inController:controller];
            return EORollbackDataSourceOperation;
        }
    }
    return EOPerformDataSourceOperation;
}

- (EODataSourceOperationDelegateResponse)controller:(EOController *)controller
    willDeleteObject:object
    inDataSource:dataSource
{
    if ([object respondsToSelector:@selector(validForDeleteInDataSource:)]) {
        NSDictionary *errors = [object validForDeleteInDataSource:dataSource];
        if (errors) {
            [self reportValidationErrors:errors forObject:object inController:controller];
            return EORollbackDataSourceOperation;
        }
    }
    return EOPerformDataSourceOperation;
}
@end


@implementation NSObject (ValidationProtocols)
// An EO can implement some or all of these methods to play a roll in its validation
- (NSArray *)keysToValidate
{
    return [NSArray array];
}

- (NSDictionary *)validForDataSource:(id <EODataSources>)dataSource;
{
    return [self validateValuesInDictionary:[self valuesForKeys:[self keysToValidate]]];
}

- (NSDictionary *)validForInsertInDataSource:(id <EODataSources>)dataSource;
{
    return [self validForDataSource:dataSource];
}

- (NSDictionary *)validForUpdateInDataSource:(id <EODataSources>)dataSource;
{
    return [self validForDataSource:dataSource];
}

- (NSDictionary *)validForDeleteInDataSource:(id <EODataSources>)dataSource;
{
    return nil;
}
@end
